#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>

#include <eastl/string.h>

//#include <XInput.h>


/// Operators required by the default EASTL allocator
void* operator new[](size_t size, const char* pName, int flags, unsigned debugFlags, const char* file, int line)
{
	return malloc(size);
}
void* operator new[](size_t size, size_t alignment, size_t alignmentOffset, const char* pName, int flags, unsigned debugFlags, const char* file, int line)
{
	return malloc(size);
}

extern"C" BOOL WINAPI SwitchToThread(void);
/*
WINBASEAPI
int
__stdcall
SwitchToThread(
							 VOID
							 );
*/
#include "Core.h"

int FrameNum;

Vec ScreenSize = Vec(640, 480);
float ScreenScale = 1.0f;
double TotalGameTime = 0.0;

int CurrentRenderPass = 0;
int RenderPasses = 1;

/// Input
void InitInput();
void DoInput();
void Input_HandleEvent(ALLEGRO_EVENT& aEvent);
void Input_GUIBegin();
void Input_GUIEnd();

void InitParticleSystem();

ALLEGRO_DISPLAY* display;
ALLEGRO_BITMAP* buffer;
ALLEGRO_TRANSFORM transform;
ALLEGRO_TRANSFORM rev_transform;

void SetTransformation(float tx, float ty, float sx, float sy, float angle)
{
	al_build_transform(&transform, tx, ty, sx, sy, angle);
	al_copy_transform(&rev_transform, &transform);
	al_invert_transform(&rev_transform);
	al_use_transform(&transform);
}

void SetDefaultTransformation()
{
	al_identity_transform(&transform);
	al_copy_transform(&rev_transform, &transform);
	al_invert_transform(&rev_transform);
	al_use_transform(&transform);
}

Vec WindowToScreen(Vec coord)
{
	al_transform_coordinates(&rev_transform, &coord.X, &coord.Y);
	return coord;
}

void DrawToScreen()
{
	al_set_target_backbuffer(display);
}

int main(int argc, char **argv)
{
	// Init
	al_init();
	al_init_primitives_addon();
	al_init_image_addon();
	al_init_font_addon();
	al_init_ttf_addon();
	al_install_mouse();
	al_install_keyboard();
	al_install_joystick();
	al_install_audio();
	al_init_acodec_addon();
	al_reserve_samples(32);
	al_reconfigure_joysticks();

	int numj = al_get_num_joysticks();

	// Event queue
	ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();


	Config();

	//al_set_new_bitmap_flags(ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
	al_set_new_display_flags(al_get_new_display_flags() | ALLEGRO_OPENGL);
	al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST);
	al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST);
	display = al_create_display(ScreenSize.X, ScreenSize.Y);

	al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);

	al_register_event_source(event_queue, al_get_display_event_source(display));
	//al_register_event_source(event_queue, al_get_timer_event_source(game.GetTimer()));
	al_register_event_source(event_queue, al_get_mouse_event_source());
	al_register_event_source(event_queue, al_get_keyboard_event_source());
	al_register_event_source(event_queue, al_get_joystick_event_source());

	al_identity_transform(&transform);
	al_identity_transform(&rev_transform);
	al_use_transform(&transform);

	/// Initialize subsystems
	srand(time(NULL));

	InitParticleSystem();

	InitInput();

	Init();

	double fps_time = 0;
	int fps_frames = 0;

	// Main event loop
	bool redraw = true;
	bool exit = false;
	while(!exit)
	{
		ALLEGRO_EVENT ev = ALLEGRO_EVENT();

		DoInput();

		while (!al_is_event_queue_empty(event_queue))
		{
			al_get_next_event(event_queue, &ev);
			Input_HandleEvent(ev);
		}

		if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN)
		{
			//XInputEnable(true);
		}
		else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT)
		{
			//XInputEnable(false);
		}
		else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
		{
			if (Quit())
				break;
		}

		static double lastTime = 0;
		static double accumulator = 0;
		if (lastTime == 0)
			lastTime = al_get_time();

		double nt = al_get_time();
		double dt = nt - lastTime;
		lastTime = nt;

		TotalGameTime += dt;

		Update((float)dt);

		//al_clear_to_color(al_map_rgb_f(0,0,0));

		Input_GUIBegin();

		for (CurrentRenderPass = 0; CurrentRenderPass < RenderPasses; CurrentRenderPass++)
			Draw();
		//al_wait_for_vsync();

		Input_GUIEnd();

		fps_time += dt;
		fps_frames += 1;
		if (fps_time >= 1.0)
		{
			char buf[1024];
			sprintf(buf, "Prototype Engine (%g FPS)", double(fps_frames)/fps_time);
			al_set_window_title(display, buf);
			fps_frames = 0;
			fps_time = 0;
		}
		
		al_flip_display();
		SwitchToThread();
	}

	al_destroy_event_queue(event_queue);

	return 0;
}

void Error(const char* error, ...)
{
	char err[1024];
	va_list ls;
	va_start(ls, error);
	vsprintf(err, error, ls);
	va_end(ls);

	eastl::string r = "Error:\n";
	r += err;
	r += "\nWould you like to debug?";
	
	if (MessageBoxA(0, r.c_str(), "Error", MB_ICONERROR|MB_YESNO) == IDYES)
		__asm int 3;
}